package com.zlyx.easy.log.aspect.abstracts;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.NonNull;

import com.zlyx.easy.core.aop.AopAspect;
import com.zlyx.easy.core.buffer.EasyBuffer;
import com.zlyx.easy.core.loggers.Logger;
import com.zlyx.easy.core.tool.StringFormat;
import com.zlyx.easy.core.utils.DateUtils;
import com.zlyx.easy.core.utils.JsonUtils;
import com.zlyx.easy.core.utils.MethodUtils;
import com.zlyx.easy.core.utils.ObjectUtils;
import com.zlyx.easy.core.utils.StringUtils;
import com.zlyx.easy.core.utils.ThreadUtils;
import com.zlyx.easy.log.annotations.EasyLog;

public abstract class AbstractLogAspect extends AopAspect {

	/**
	 * 是否记录切面日志
	 */
	@Value("${easy.log.is-show-log:true}")
	protected boolean isShowLog;

	/**
	 * 排除的路径
	 */
	@Value("${easy.log.exclude-urls:true}")
	protected List<String> excludeUrls;

	/**
	 * 执行切面事件
	 * 
	 * @param pjp
	 * @param url
	 * @param todo
	 * @return
	 * @throws Throwable
	 */
	protected Object doAround(ProceedingJoinPoint pjp, String url, String... todo) throws Throwable {
		// 判断类级别关闭
		EasyLog easyLog = realClass.getAnnotation(EasyLog.class);
		if (easyLog != null && Boolean.FALSE == easyLog.isLog()) {
			return pjp.proceed();
		}
		// 如果请求路径被排除，那么不执行后续逻辑
		if (excludeUrls.contains(url)) {
			return pjp.proceed();
		}
		if (Boolean.FALSE == doValidate(realMethod, pjp.getArgs())) {
			Logger.err(realClass, realMethod.getName() + "@参数校验不通过!");
			return null;
		}
		return isShowLog
				? doPrint(pjp, realClass, realMethod, StringUtils.defaultOfEmpty(url, realMethod.getName()), todo)
				: doProcess(pjp, realClass, realMethod);
	}

	/**
	 * 打印参数日志
	 * 
	 * @param pjp
	 * @param realClass
	 * @param realMethod
	 * @param url
	 * @param todos
	 * @return
	 * @throws Throwable
	 */
	protected Object doPrint(ProceedingJoinPoint pjp, Class<?> realClass, Method realMethod, String url, String[] todos)
			throws Throwable {
		Object[] args = pjp.getArgs();
		if (ObjectUtils.isEmpty(todos)) {
			todos = new String[] { realClass.getName(), realMethod.getName() };
		}
		String description = StringUtils.defaultOfEmpty(StringFormat.format(todos), realMethod.getName());
		MDC.put("traceId", description);

		Logger.info(realClass,
				EasyBuffer.newBuffer("【", ThreadUtils.getCode(), "】")
						.append("\n=========================请求开始处理=========================", "\n")
						.append("开始时间 =>  ", DateUtils.getNowMs(), "\n").append("请求描述 =>  ", description, "\n")
						.append("请求路径 =>  ", url, "\n")
						.append("请求方法 =>  ", realClass.getName(), ".", realMethod.getName(), "\n")
						.append("请求参数 =>  ", MethodUtils.getParamsMap(realMethod, args), "\n").append("请求执行 =>  ", "\n")
						.toString());
		Object res = doProcess(pjp, realClass, realMethod);
		Logger.info(realClass,
				EasyBuffer.newBuffer("【", ThreadUtils.getCode(), "】").append("\n请求结果 =>  ", JsonUtils.toJson(res), "\n")
						.append("结束时间 =>  ", DateUtils.getNowMs(), "\n")
						.append("=========================请求结束处理=========================\n").toString());
		return res;
	}

	@Override
	protected boolean doValidate(Parameter parameter, Object arg) {
		return (!parameter.isAnnotationPresent(NonNull.class) && !parameter.isAnnotationPresent(NotNull.class)
				&& !parameter.isAnnotationPresent(NotEmpty.class) && !parameter.isAnnotationPresent(NotBlank.class))
				|| ObjectUtils.isNotEmpty(arg);
	}

	/**
	 * 是否返回异常信息
	 * 
	 * @return
	 */
	public boolean isReturnException() {
		return Map.class.isAssignableFrom(returnType);
	}
}
